import { ClickUp } from "./ClickUpAPI";
import { ITaskCreation, Status, ITag, Priorities } from "./ClickUpInterfaces";
import config from "./config";
import Gitlab from "gitlab";

interface IGitlabUser {
  id: number;
  name: string;
  username: string;
  state: string;
}

interface IIssue {
  id: number;
  iid: number;
  project_id: number;
  title: string;
  description: string;
  state: "opened" | "closed";
  created_at: Date;
  updated_at: Date;
  closed_at: null | Date;
  closed_by: null | Date;
  labels: string[];
  assignees: IGitlabUser[];
  author: IGitlabUser;
  assignee: IGitlabUser;
  due_date: null | Date;
  web_url: string;
  time_stats: {
    time_estimate: number; // seconds
    total_time_spent: number; // seconds
  };
  notes: IIssueNote[];
}

interface IIssueNote {
  body: string;
  author: IGitlabUser;
  created_at: Date;
  updated_at: Date;
  system: boolean;
}

async function migrate() {
  const api = new Gitlab({
    token: config.gitlabConfig.token
  });

  const clickUp = new ClickUp(
    config.clickUpConfig.publicKey,
    config.clickUpConfig.bearerToken
  );
  const teams = await clickUp.getTeams();

  const loneStoneTeam = teams.find(t => t.name === "Lone Stone");
  if (!loneStoneTeam) {
    throw new Error("Team not found");
  }

  const sonlySpace = (await clickUp.getTeamSpaces(loneStoneTeam.id)).find(
    s => s.name === "StartupOnly"
  );
  if (!sonlySpace) {
    throw new Error("Space not found");
  }

  const refonteProject = (await clickUp.getSpaceProjects(sonlySpace.id)).find(
    p => p.name === "Refonte"
  );
  if (!refonteProject) {
    throw new Error("Project not found");
  }

  const clickUpUsers = await clickUp.getTeamUsers(loneStoneTeam.id);

  // First we create a few Lists if needed
  const tasksList =
    refonteProject.lists.find(l => l.name === "Tasks") ||
    (await clickUp.createList({ name: "Tasks" }, refonteProject.id));

  const issuesList =
    refonteProject.lists.find(l => l.name === "Issues") ||
    (await clickUp.createList({ name: "Issues" }, refonteProject.id));

  // Then we GET the issues from Gitlab
  const issues = (await api.Issues.all({
    projectId: config.gitlabConfig.projectId
  })) as IIssue[];

  for (const issue of issues) {
    const newTags: ITag[] = [];

    const assignee = issue.assignee
      ? clickUpUsers.filter(u => u.username != null).find(u => {
          const match = u.username.match(issue.assignee.name);
          return match != null && match.length !== 0;
        })
      : null;

    // Adding tags
    for (const label of issue.labels.filter(
      l => l !== "Done" && l !== "A vérifier par client"
    )) {
      if (!newTags.some(t => t.name === label)) {
        newTags.push({
          name: label,
          tag_bg: intToRGB(hashCode(label)),
          tag_fg: intToRGB(hashCode(label))
        });
      }
    }

    const priority = nameToPriority(issue.title);

    const taskPayload: ITaskCreation = {
      name: issue.title.replace(/\*+/, ""),
      content: issue.description,
      assignees: assignee ? [assignee.id.toString()] : undefined,
      status: gitlabToClickupStatus(issue),
      tags: newTags,
      priority
    };

    const task = await clickUp.createTask(taskPayload, issuesList.id);

    // For each issue we also get their notes
    issue.notes = await api.IssueNotes.all(
      config.gitlabConfig.projectId,
      issue.iid
    );

    // For each non system note we comment
    // TODO: Find a way to keep the original user
    for (const note of issue.notes.filter(n => !n.system)) {
      await clickUp.commentTask(task.id, {
        attachment: [],
        comment: [{ text: note.body }]
      });
    }
  }
}

const gitlabToClickupStatus = (issue: IIssue): Status => {
  if (issue.state === "closed") {
    return Status.Closed;
  }

  if (issue.labels.includes("Done")) {
    return Status.InProgress;
  }

  if (issue.labels.includes("A vérifier par client")) {
    return Status.InReview;
  } else {
    return Status.Open;
  }
};

const nameToPriority = (issueName: string): Priorities => {
  const match = issueName.split("*");
  if (!match) {
    return Priorities.Low;
  }

  switch (match.length) {
    case 0:
    case 1:
      return Priorities.Low;

    case 2:
      return Priorities.Normal;

    case 3:
      return Priorities.High;

    case 4:
    case 5:
      return Priorities.Urgent;

    default:
      return Priorities.Low;
  }
};

migrate().catch(e => console.error(e));

function hashCode(str) {
  // java String#hashCode
  let hash = 0;
  for (let i = 0; i < str.length; i++) {
    // tslint:disable-next-line:no-bitwise
    hash = str.charCodeAt(i) + ((hash << 5) - hash);
  }
  return hash;
}

function intToRGB(i) {
  // tslint:disable-next-line:no-bitwise
  const c = (i & 0x00ffffff).toString(16).toUpperCase();

  return "00000".substring(0, 6 - c.length) + c;
}
